using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace UMC.ITME
{
    class RelayAsyncArgs : IDisposable
    {

        public class Relay
        {
            public Socket Socket;
            public bool IsConnect;
            public Relay(Socket socket)
            {
                httpMimes.AddOrUpdate(this.GetHashCode(), this, (k, l) => this);
                this.ActiveTime = Utility.TimeSpan();
                this.Socket = socket;
                this.IsConnect = true;
            }

            public uint ActiveTime
            {
                get;
                set;
            }
        }
        public static void CheckTimeOut()
        {
            var time = UMC.Data.Utility.TimeSpan();
            var ms = httpMimes.Values.ToArray();

            foreach (var b in ms)
            {
                try
                {

                    if ((b.ActiveTime + 30) < time)
                    {
                        httpMimes.TryRemove(b.GetHashCode(), out var _);
                        b.IsConnect = false;
                        b.Socket.Close();
                        b.Socket.Dispose();
                    }
                }
                catch
                {

                }
            }
        }
        static ConcurrentDictionary<int, Relay> httpMimes = new ConcurrentDictionary<int, Relay>();

        byte[] receiveData = System.Buffers.ArrayPool<byte>.Shared.Rent(512);
        Relay input, output;
        public int ConfigKey
        {
            get; set;
        }
        public bool IsSend
        {
            get; set;
        }
        int _SendQty = 0;
        public RelayAsyncArgs(Relay input, Relay output)
        {
            this.input = input;
            this.output = output;
            SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
            eventArgs.SetBuffer(receiveData);
            eventArgs.Completed += Http;
            if (!this.input.Socket.ReceiveAsync(eventArgs))
            {
                Task.Run(() => ProcessReceive(eventArgs));
            }

        }

        private void Http(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Receive:
                        ProcessReceive(e);
                        break;
                    case SocketAsyncOperation.Send:
                        if (this.receiveData.Length > 0 && this.input.IsConnect)
                        {
                            e.SetBuffer(this.receiveData);
                            this.output.ActiveTime = Utility.TimeSpan();
                            if (!this.input.Socket.ReceiveAsync(e))
                            {
                                ProcessReceive(e);
                            }
                            break;
                        }
                        this.output.IsConnect = false;
                        this.output.Socket.Close();
                        this.input.Socket.Close();
                        e.Dispose();
                        this.Close();
                        break;
                    default:
                        this.output.Socket.Close();
                        this.input.Socket.Close();
                        e.Dispose();
                        Close();
                        break;

                }
            }
            catch
            {
                this.Close();
            }
        }
        void Close()
        {
            this.input.IsConnect = false;

            if (receiveData.Length > 0)
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(receiveData);
                receiveData = Array.Empty<byte>();
                if (this.ConfigKey != 0)
                {
                    Bridge.HttpBridge.Statistical(this.ConfigKey, this.IsSend ? _SendQty : 0, this.IsSend ? 0 : _SendQty);
                }
            }
        }
        // bool IsSending = true;
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                this.input.ActiveTime = Utility.TimeSpan();
                _SendQty += e.BytesTransferred;
                e.SetBuffer(this.receiveData, e.Offset, e.BytesTransferred);

                output.ActiveTime = Utility.TimeSpan();
                try
                {
                    if (this.output.IsConnect)
                    {
                        // IsSending = true;
                        if (!output.Socket.SendAsync(e))
                        {
                            // IsSending = false;
                            if (this.input.IsConnect == false)
                            {
                                this.output.IsConnect = false;
                                this.output.Socket.Close();

                                e.Dispose();
                                this.Close();
                                this.input.Socket.Close();
                            }
                            else
                            {
                                if (this.receiveData.Length > 0)
                                {
                                    e.SetBuffer(this.receiveData);
                                    if (!this.input.Socket.ReceiveAsync(e))
                                    {
                                        ProcessReceive(e);
                                    }
                                }
                            }

                        }
                    }
                    else
                    {
                        this.output.Socket.Close();
                        this.input.IsConnect = true;
                        this.input.Socket.Close();
                        e.Dispose();
                        this.Close();
                    }
                }
                catch
                {
                    e.Dispose();
                    this.Close();
                }
            }
            else
            {

                this.input.IsConnect = false;
                this.output.IsConnect = false;
                e.Dispose();
                this.Close();
                this.input.Socket.Close();
                // if (IsSending == false)
                // {

                //     this.output.Socket.Close();// = false;
                // }
            }
        }

        public void Dispose()
        {
            Close();
        }
    }
}